# 导入PyTorch库，用于构建深度学习模型、自动求导和参数优化
import torch
# 导入numpy库，用于读取和处理csv格式的数据集（如糖尿病数据）
import numpy as np
# 导入matplotlib.pyplot库，用于绘制训练过程中的损失变化曲线
import matplotlib.pyplot as plt
# 导入sympy的exp_polar（当前代码未实际使用，可能是冗余导入，可删除）
from sympy import exp_polar

# 读取糖尿病数据集：
# 1. np.loadtxt：读取csv.gz压缩文件（需确保文件路径正确）
# 2. delimiter=","：指定csv文件的分隔符为逗号
# 3. dtype=np.float32：将数据类型设为32位浮点数，与PyTorch默认参数类型匹配，避免类型不匹配
xy = np.loadtxt("diabetes.csv.gz", delimiter=",", dtype=np.float32)
# 提取输入特征x_data：取所有行（所有样本）、前8列（糖尿病数据集的8个特征，如血糖、BMI等）
# torch.from_numpy：将numpy数组转为PyTorch张量，保留数据类型和形状
x_data = torch.from_numpy(xy[:, 0:-1])  # 形状为(N, 8)，N为样本总数，8为特征数
# 提取目标标签y_data：取所有行、最后1列（标签：1表示患病，0表示未患病）
# xy[:, [-1]]：用双层括号确保形状为(N, 1)（单输出），而非(N,)（避免后续维度错误）
y_data = torch.from_numpy(xy[:, [-1]])  # 形状为(N, 1)，与模型输出维度匹配

# 定义多维度逻辑回归模型（实际是4层全连接神经网络，用于二分类）
class MDModel(torch.nn.Module):
    # 构造函数：初始化模型的层结构和激活函数
    def __init__(self):
        # 调用父类Module的构造函数，继承参数管理、前向传播等核心功能
        super(MDModel, self).__init__()
        # 第1层全连接：输入特征数=8（匹配x_data的8个特征），输出特征数=6（压缩维度）
        self.linear1 = torch.nn.Linear(8, 6)
        # 第2层全连接：输入=6（承接上一层输出），输出=4（继续压缩维度）
        self.linear2 = torch.nn.Linear(6, 4)
        # 第3层全连接：输入=4，输出=2（进一步压缩到低维度）
        self.linear3 = torch.nn.Linear(4, 2)
        # 第4层全连接：输入=2，输出=1（最终输出单值，用于后续Sigmoid映射为概率）
        self.linear4 = torch.nn.Linear(2, 1)
        # 定义激活函数：Sigmoid（将线性输出映射到[0,1]，适合二分类概率输出）
        self.activation = torch.nn.Sigmoid()

    # 前向传播函数：定义数据在模型中的计算流程（输入x → 输出预测概率）
    def forward(self, x):
        # 第1层：线性变换 → Sigmoid激活（将8维输入转为6维特征）
        x = self.activation(self.linear1(x))
        # 第2层：线性变换 → Sigmoid激活（6维→4维）
        x = self.activation(self.linear2(x))
        # 第3层：线性变换 → Sigmoid激活（4维→2维）
        x = self.activation(self.linear3(x))
        # 第4层：线性变换 → Sigmoid激活（2维→1维概率，最终输出）
        x = self.activation(self.linear4(x))
        return x

# 创建模型实例（自动初始化所有全连接层的权重w和偏置b）
model = MDModel()

# 定义损失函数：二元交叉熵损失（BCELoss），专为二分类任务设计
# size_average=True（已过时，建议改为reduction='mean'）：对所有样本的损失求平均，使损失值更易解读
criterion = torch.nn.BCELoss(size_average=True)

# 定义优化器：随机梯度下降（SGD），用于更新模型所有可训练参数
# model.parameters()：自动获取所有全连接层的w和b
# lr=0.1：学习率（步长较大，加速前期收敛；可根据损失变化调整，避免后期震荡）
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

# 初始化列表：存储训练轮次和对应损失值，用于后续可视化
epoch_list = []  # 记录每一轮的序号
loss_list = []   # 记录每一轮的平均损失

# 模型训练循环：迭代100000轮（超多样本时可减少轮次，避免过拟合；此处轮次较多为确保收敛）
for epoch in range(1000):
    # 1. 前向传播：将输入x_data传入模型，得到所有样本的预测概率y_pred（形状(N,1)）
    y_pred = model(x_data)
    # 2. 计算损失：对比y_pred（预测概率）和y_data（真实标签），得到当前轮次的平均损失
    loss = criterion(y_pred, y_data)
    # 打印当前轮次和损失值（观察损失是否逐步下降，判断模型是否收敛）
    print("epoch:{}, loss:{}".format(epoch, loss))
    # 将轮次和损失值加入列表，用于后续绘图
    epoch_list.append(epoch)
    loss_list.append(loss.data.item())  # .item()：将张量转为Python数值，避免冗余信息

    # 3. 清零梯度：优化器默认累积梯度，需先清零，防止上一轮梯度干扰当前更新
    optimizer.zero_grad()
    # 4. 反向传播：自动计算损失对所有参数（w和b）的梯度（链式法则）
    loss.backward()
    # 5. 参数更新：根据梯度和学习率，更新所有参数（w = w - lr*dw，b = b - lr*db）
    optimizer.step()

# 绘制损失变化曲线：x轴为训练轮次，y轴为平均损失
plt.plot(epoch_list, loss_list)
# 显示图像（观察损失是否逐步下降至平稳，判断训练效果）
plt.show()